package framework;

import java.util.Timer;
import java.util.TimerTask;

import javax.naming.NamingException;

import org.junit.Assert;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.Keys;
import org.openqa.selenium.TimeoutException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.WebDriverWait;

import framework.PropertiesResourceManager;

import framework.elements.Label;

/**
 * РћСЃРЅРѕРІРЅРѕР№ РєР»Р°СЃСЃ РґР»СЏ РґРѕСЃС‚СѓРїР° Рє Р±СЂР°СѓР·РµСЂСѓ, СЂР°СЃС€РёСЂСЏСЋС‰РёР№ РІРѕР·РјРѕР¶РЅРѕСЃС‚Рё СЃС‚Р°РЅРґР°СЂС‚РЅРѕРіРѕ DefaultSelenium
 */
public class Browser {

	private static final long SLEEP_DEFAULT = 3000l;
	private static final String DEFAULT_CONDITION_TIMEOUT = "defaultConditionTimeout";
	private static final String DEFAULT_PAGE_LOAD_TIMEOUT = "defaultPageLoadTimeout";
	private static final String DEFAULT_REPEAT_TIMEOUT = "defaultRepeatTimeout";
	private static final String URL_LOGIN_PAGE = "urlLoginPage";
	private static final String DEFAULT_HOSTNAME = "hostname";
	//private static final String DEFAULT_PORT = "port";

	// РёРјСЏ С„Р°Р№Р»Р° СЃ РЅР°СЃС‚СЂРѕР№РєР°РјРё Selenium
	/**
	 * speedValue=100 defaultPageLoadTimeout=60 defaultConditionTimeout=180 urlLoginPage=http://opensesame:%23pears0n@dev.pearsoninnovationlabs.com/ #overrided in
	 * Browser.initProperties-default=firefox; #if null - use argument 'browser' to JVM; if other value (without '*'), #will use it as browserStartCommand #Usage: #firefox #iexplore #chrome
	 * #null browser=iexplore
	 */
	static final String PROPERTIES_FILE = "selenium.properties";
	private static final String BROWSER_BY_DEFAULT = "firefox";
	private static final String BROWSER_PROP = "browser";

	// browsers
	private static Browser instance;
	private static RemoteWebDriver driver;
	public static PropertiesResourceManager props;
	// РїРѕР»СЏ РєР»Р°СЃСЃР°
	private static String browserURL;
	private static String timeoutForPageLoad;
	private static String timeoutForCondition;
	public static Browsers currentBrowser;

	/**
	 * РџСЂРёРІР°С‚РЅС‹Р№ РєРѕРЅСЃС‚СЂСѓРєС‚РѕСЂ (РїР°С‚С‚РµСЂРЅ СЃРёРЅРіР»С‚РѕРЅ)
	 */
	private Browser() {
		Logger.getInstance().info(String.format("Браузер '%s' готов", currentBrowser.toString()));
	}

	public static Browser getInstance() {
		if (instance == null) {
			initProperties();
			try {
				driver = BrowserFactory.setUp(currentBrowser.toString());
				Logger.getInstance().info("Запускается браузер...");
			} catch (NamingException e) {
				Logger.getInstance().info("NamingException...");
				e.printStackTrace();
			}
			instance = new Browser();
		}
		return instance;
	}

	/**
	 * Р РµР°Р»РёР·Р°С†РёСЏ Р·Р°РєСЂС‹С‚РёСЏ Р±СЂР°СѓР·РµСЂР° (РњРѕР¶РЅРѕ РґРѕРїРѕР»РЅРёС‚РµР»СЊРЅРѕ РІСЃС‚Р°РІРёС‚СЊ СѓР±РёРІР°РЅРёРµ РїСЂРѕС†РµСЃСЃРѕРІ)
	 */
	public void exit() {
		try {
			driver.quit();
			Logger.getInstance().info("Браузер закрывается...");
		} catch (Exception e) {
			// Logger.getInstance().warn("ex..." + e.getMessage());
		} finally {
			instance = null;
			// Logger.getInstance().info("instance = null...");
		}
	}

	/*
	 * ==================================================================================== Р—Р°Р±РёСЂР°РµРј РїСЂРѕРїРµСЂС‚Рё
	 * ====================================================================================
	 */
	public String getTimeoutForCondition() {
		return timeoutForCondition;
	}

	public String getTimeoutForPageLoad() {
		return timeoutForPageLoad;
	}

	public String getStartBrowserURL() {
		return browserURL;
	}

	/**
	 * init
	 */
	private static void initProperties() {

		props = new PropertiesResourceManager(PROPERTIES_FILE);
		timeoutForPageLoad = props.getProperty(DEFAULT_PAGE_LOAD_TIMEOUT);
		timeoutForCondition = props.getProperty(DEFAULT_CONDITION_TIMEOUT);

		browserURL = props.getProperty(URL_LOGIN_PAGE).replace("hostname", getHostname());

		if (props.getProperty(BROWSER_PROP).equalsIgnoreCase("null")) {
			// using System.getProperty
			currentBrowser = Browsers.valueOf(System.getProperty(BROWSER_PROP, BROWSER_BY_DEFAULT).toUpperCase());
		} else {
			// using selenium.properties
			String proper = props.getProperty(BROWSER_PROP);
			currentBrowser = Browsers.valueOf(proper.toUpperCase());
		}
	}

	public static String getHostname() {
		String hostname = System.getProperty("hostname");
		//String port = System.getProperty("port");
		if(hostname == null)
			hostname = props.getProperty(DEFAULT_HOSTNAME);
			System.setProperty("hostname",hostname);
			return hostname;
	}

	/**
	 * РћР¶РёРґР°РµРј Р·Р°РіСЂСѓР·РєСѓ СЃС‚СЂР°РЅРёС†С‹ (РїРѕ Javascript readyState)
	 */
	public void waitForPageToLoad() {
		// Logger.getInstance().info("waitForPageToLoad started");
		WebDriverWait wait = new WebDriverWait(driver, Long.parseLong(getTimeoutForPageLoad()));
		try {
			wait.until((ExpectedCondition<Boolean>) new ExpectedCondition<Boolean>() {
				public Boolean apply(WebDriver driver) {
					if (!(driver instanceof JavascriptExecutor)) {
						return true;
					}
					Object result = ((JavascriptExecutor) driver)
							.executeScript("return document['readyState'] ? 'complete' == document.readyState : true");
					if (result != null && result instanceof Boolean && (Boolean) result) {
						return true;
					}
					return false;
				}
			});
		} catch (Exception e) {
			Logger.getInstance().warn("Не удалось дождаться загрузки страницы");
			/*Logger.getInstance().info("Ввод URL: " + getDriver().getCurrentUrl());
			getDriver().navigate().to(getDriver().getCurrentUrl());*/
		}
		// Logger.getInstance().info("waitForPageToLoad ended");
	}

	/**
	 * Ждет появления нового окна. Если окно не появилось тест будет завершен.
	 * 
	 * @param prevWndCount количество окон до появления нового
	 * 
	 */
	public void waitForNewWindow(int prevWndCount) {
		final int wndCount = prevWndCount;
		WebDriverWait wait = new WebDriverWait(driver, Long.parseLong(getTimeoutForCondition()));
		try {
			wait.until((ExpectedCondition<Boolean>) new ExpectedCondition<Boolean>() {
				public Boolean apply(WebDriver d) {
					return d.getWindowHandles().size() > wndCount;
				}
			});
		} catch (Exception e) {
			Assert.assertTrue("Новое окно не открылось", false);
		}
	}
	
	/**
	 * Ждет появления нового окна.
	 * @param prevWndCount количества окон до появления нового окна
	 * @return true, если новое окно появилось
	 */
	public Boolean waitForNewWindowWithoutAssertion(final int prevWndCount) {
		Boolean isAppears = true;
		final int wndCount = prevWndCount;
		Long timeout = Long.parseLong(props.getProperty(DEFAULT_REPEAT_TIMEOUT));
		WebDriverWait wait = new WebDriverWait(driver, timeout);
		try {
			wait.until((ExpectedCondition<Boolean>) new ExpectedCondition<Boolean>() {
				public Boolean apply(WebDriver d) {
					return d.getWindowHandles().size() > wndCount;
				}
			});
		} catch (TimeoutException e) {
			isAppears = false;
		}
		return isAppears;
	}
	
	public void waitForWindowIsCloseAndSwitch(int prevWndCount) {
		final int wndCount = prevWndCount;
		WebDriverWait wait = new WebDriverWait(driver, Long.parseLong(getTimeoutForCondition()));
		try {
			wait.until((ExpectedCondition<Boolean>) new ExpectedCondition<Boolean>() {
				public Boolean apply(final WebDriver d) {
					return d.getWindowHandles().size() < wndCount;
				}
			});
		} catch (Exception e) {
			Assert.assertTrue("Старое окно не закрылось", false);
		}
		driver.switchTo().window((String) driver.getWindowHandles().toArray()[wndCount - 2]);
	}
	/** Ждет пока окно закроется.
	 * @param prevWndCount количество окон до закрытия
	 * @return true если окно закрылось
	 */
	public Boolean waitForWndIsCloseWithoutAssertion(final int prevWndCount){
		final int wndCount = prevWndCount;
		Boolean isClosed = true;
		Long timeout = Long.parseLong(props.getProperty(DEFAULT_REPEAT_TIMEOUT));
		WebDriverWait wait = new WebDriverWait(driver, timeout);
		try {
			wait.until((ExpectedCondition<Boolean>) new ExpectedCondition<Boolean>() {
				public Boolean apply(final WebDriver d) {
					return d.getWindowHandles().size() < wndCount;
				}
			});
		} catch (Exception e) {
			isClosed = false;
		}
		return isClosed;
	}
	/** Закрывает окно и переключается на предыдущее.
	 * @param currentWndCount
	 */
	public void closeWindowAndSwitch() {
		final int wndCount = driver.getWindowHandles().size();
		String handle = driver.getWindowHandle();
		WebDriver wd = driver.switchTo().window(handle);
		wd.close();
		WebDriverWait wait = new WebDriverWait(driver, Long.parseLong(getTimeoutForCondition()));
		try {
			wait.until((ExpectedCondition<Boolean>) new ExpectedCondition<Boolean>() {
				public Boolean apply(final WebDriver d) {
					return d.getWindowHandles().size() < wndCount;
				}
			});
		} catch (TimeoutException e) {
			Assert.assertTrue("Окно не закрылось", false);
		}
		selectLastWindow();
	}
	/**
	 * Р’С‹Р±РёСЂР°РµРј РїСЂРµРґС‹РґСѓС‰РµРµ РѕРєРЅРѕ (РїРѕ СЃРїРёСЃРєСѓ С…РµРЅРґР»РµСЂРѕРІ).
	 */
	public void selectPreviousWindow() {
		Object[] handles = driver.getWindowHandles().toArray();
		String handle = driver.getWindowHandle();
		Assert.assertTrue("Не удалось переключиться на предыдущее окно", handles.length > 1);
		for (int i = 1; i < handles.length; i++) {
			if (handles[i].equals(handle)) {
				driver.switchTo().window((String) handles[i - 1]);
				return;
			}
		}
		driver.switchTo().window((String) handles[handles.length - 2]);

	}
	/**
	 * Р’С‹Р±РёСЂР°РµРј РїРµСЂРІРѕРµ РѕРєРЅРѕ (РїРѕ СЃРїРёСЃРєСѓ С…РµРЅРґР»РµСЂРѕРІ).
	 */
	public void selectFirstWindow() {
		Object[] handles = driver.getWindowHandles().toArray();
		driver.switchTo().window((String) handles[0]);

	}

	/**
	 * РћР¶РёРґР°РµРј РїРѕСЏРІР»РµРЅРёСЏ РЅРѕРІРѕРіРѕ РѕРєРЅР° Рё РІС‹Р±РёСЂР°РµРј РµРіРѕ
	 */
	public void selectNewWindow() {
		Object[] handles = driver.getWindowHandles().toArray();
		waitForNewWindow(handles.length);
		handles = driver.getWindowHandles().toArray();
		driver.switchTo().window((String) handles[handles.length - 1]);

	}

	/**
	 * Р’С‹Р±РёСЂР°РµРј РїРѕСЃР»РµРґРЅРµРµ РѕРєРЅРѕ (РїРѕ СЃРїРёСЃРєСѓ С…РµРЅРґР»РµСЂРѕРІ)
	 */
	public void selectLastWindow() {
		Object[] handles = driver.getWindowHandles().toArray();
		driver.switchTo().window((String) handles[handles.length - 1]);
	}

	/**
	 * РџРµСЂРµС…РѕРґРёРј РЅР° СЃС‚Р°СЂС‚РѕРІСѓСЋ СЃС‚СЂР°РЅРёС†Сѓ
	 */
	public void openStartPage() {
		driver.navigate().to(props.getProperty(getStartBrowserURL()));
	}

	/**
	 * РњР°РєСЃРёРјРёР·РёСЂСѓРµС‚ РѕРєРЅРѕ <s>(РЅРµ РїР°С€РµС‚ РЅРёРіРґРµ, РєСЂРѕРјРµ FF 3.6)</s>
	 * <p>
	 * wrong, works on IE8, IE9 also
	 */
	public void windowMaximise() {
		driver
				.executeScript("if (window.screen) {window.moveTo(0, 0);window.resizeTo(window.screen.availWidth,window.screen.availHeight);};");
	}

	/**
	 * РџРµСЂРµС…РѕРґРёРј РїРѕ РЅРµРѕР±С…РѕРґРёРјРѕРјСѓ Р°РґСЂРµСЃСѓ
	 */
	public void navigate(String URL) {
		driver.navigate().to(URL);
	}

	/**
	 * Getter РґР»СЏ РґРѕРїРѕР»РЅРёС‚РµР»СЊРЅС‹С… РґРµР№СЃС‚РІРёР№ СЃ RemoteWebDriver (Keybord,Mouse, Рё С‚.Рґ.)
	 */
	public RemoteWebDriver getDriver() {
		return driver;
	}

	/**
	 * Open new window by hot keys Ctrl + N, webdriver switch focus on it.
	 */
	public void openNewWindow() {
		getDriver().findElement(By.xpath("//body")).sendKeys(Keys.CONTROL, "n");
		Object[] headers = getDriver().getWindowHandles().toArray();
		getDriver().switchTo().window(headers[headers.length - 1].toString());
	}

	public void click(By selector) {
		new Label(selector, "Label").click();
	}

	public void waitForIsElementPresent(By selector) {
		new Label(selector, "Label").waitAndAssertIsPresent();
	}

	public String getLocation() {
		return driver.getCurrentUrl();
	}

	public void jsExecute(String script) {
		JavascriptExecutor js = (JavascriptExecutor)getDriver();
		js.executeScript(script);
	}

	public void sleep(long mSeconds) {
		try {
			Thread.sleep(mSeconds);
		} catch (InterruptedException e1) {
			e1.printStackTrace();
		}
	}
	
	public void sleep(){
		sleep(SLEEP_DEFAULT);
	}
	
	public enum Browsers {
		FIREFOX("firefox"), IEXPLORE("iexplore"), CHROME("chrome");
		public String value;

		Browsers(String value) {
			this.value = value;
		}

		public String toString() {
			return value;
		}
	}
}
